package se.webbzon.boltzmann.minigame.chemistry;

import java.awt.Image;
import java.awt.Rectangle;
import java.awt.geom.AffineTransform;

import se.webbzon.boltzmann.minigame.sandbox.SandBox;
import se.webbzon.boltzmann.minigame.sandbox.SandBoxObject;
import se.webbzon.oschi01.resourceset.ResourceSet;
import se.webbzon.oschi01.worldarea.WorldArea;
import se.webbzon.oschi01.worldcamera.WorldCameraPane;
import se.webbzon.oschi01.worldobject.WorldObject;

public abstract class AbstractMolecule extends SandBoxObject {
	
	// The 2-dimensional depth of the molecule
	private static final int depth = 25;
	
	private static final int minLifetime = 300;
	private static final int maxLifetime = 1200;
	private static final double maxRotationSpeed = 0.02;
	private static final double minSpeed = 0.2;
	private static final double maxSpeed = 0.6;
	
	// The sand box to which this molecule is attached
	private SandBox box;
	
	// The initial and remaining lifetime of this molecule
	private int lifetime, remaining;
	
	// The rotation of the molecule
	private double theta, w;
	
	// The velocity of the molecule
	private double vx, vy;
	
	/*============================================================
	Constructors
	============================================================*/

	public AbstractMolecule() {
		this(minLifetime + (int) (Math.random() * (maxLifetime - minLifetime)));
	}
	
	/** Creates a new abstract molecule with the provided initial lifetime. 
	 * If the initial lifetime is -1 then the lifetime of the molecule is
	 * infinite. **/
	public AbstractMolecule(int lifetime) {
		super(WorldObject.onStep + WorldObject.onPostDraw, depth);
		this.lifetime = lifetime;
		setSize(24,24);
		theta = 2 * Math.PI * Math.random();
		drop();
	}
	
	/*============================================================
	Public Methods
	============================================================*/
	
	/** This method is invoked when this molecule should be destroyed. **/
	public abstract void destroy(SandBox box);
	
	/** Returns the lifetime of this molecule. **/
	public int getLifetime() {
		return lifetime;
	}
	
	/** Sets the lifetime of this molecule. **/
	public void setLifetime(int lifetime) {
		this.lifetime = lifetime;
		this.remaining = lifetime;
	}
	
	/** Sets the velocity of the molecule. **/
	public void setVelocityDirection(double dir) {
		double v = Math.hypot(vx, vy);
		vx = v * Math.cos(dir);
		vy = v * Math.sin(dir);
	}
	
	/** Returns the x-velocity of the molecule. **/
	public double getXVelocity() {
		return vx;
	}
	
	/** Returns the y-velocity of the molecule. **/
	public double getYVelocity() {
		return vy;
	}
	
	/** Grabs the molecule. **/
	public void grab() {
		vx = vy = w = 0;
		remaining = -1;
	}
	
	/** Drops the molecule. **/
	public void drop() {
		w = maxRotationSpeed * (2 * Math.random() - 1);
		double angle = 2 * Math.PI * Math.random();
		double speed = minSpeed + Math.random() * (maxSpeed - minSpeed);
		vx = Math.cos(angle) * speed;
		vy = Math.sin(angle) * speed;
		remaining = lifetime;
	}
	
	@Override public void setSize(double width, double height) {
		super.setSize(width, height);
	}
	
	@Override public void onAttach(SandBox box) {
		this.box = box;
		remaining = lifetime;
	}

	@Override public void onDetach(SandBox box) {
		this.box = null;
	}
	
	@Override public void onStep(WorldArea world) {
		if (remaining > 0)
			remaining--;
		else if (remaining == 0)
			destroy(box);
		
		theta += w;
		if (theta > 2 * Math.PI)
			theta -= 2 * Math.PI;
		else if (theta < - 2 * Math.PI)
			theta += 2 * Math.PI;
		
		if (box != null) {
			Rectangle d = new Rectangle();
			box.getBoundary(d);
			if (getX() + getXVelocity() > d.getWidth() || getX() + getXVelocity() < 0)
				vx = -vx;
			if (getY() + getYVelocity() > d.getHeight() || getY() + getYVelocity() < 0)
				vy = -vy;
			setPosition(getX() + getXVelocity(), getY() + getYVelocity());
		}
	}

	@Override public void onPostDraw(WorldArea world, WorldCameraPane hud) {
		AffineTransform trans = new AffineTransform();
		trans.translate(getX(), getY());
		trans.rotate(theta);
		int w = (int) getWidth();
		int h = (int) getWidth();
		hud.setTransform(trans);
		hud.drawImage(getImage().get(0), -w/2, -h/2, w, h);
		hud.resetTransform();
	}
	
	/*============================================================
	Protected Methods
	============================================================*/
	
	/** Returns the resource set containing the image of the molecule. **/
	protected abstract ResourceSet<Image> getImage();

}
